/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.example.previewpicture.rec;

import ohos.agp.components.*;
import ohos.agp.utils.LayoutAlignment;
import ohos.agp.utils.Point;
import ohos.agp.window.service.Display;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.bundle.AbilityInfo;

import java.util.HashMap;
import java.util.List;

/**
 * EasyGridProvider.
 *
 * @author author
 * @version version
 */
public abstract class EasyGridProvider<T> extends BaseItemProvider {
    /**
     * 默认的布局类型.
     */
    public static final int ITEM_DEFAULT_TYPE = 0;
    /**
     * 空布局.
     */
    public static final int ITEM_EMPTY_TYPE = -1;
    /**
     * Context.
     */
    protected Context mContext;
    /**
     * numColumns.
     */
    protected int numColumns = 1;
    /**
     * gridSpacing.
     */
    protected int gridSpacing = 0;
    private List<T> data;
    private int displayWidth;
    private OnItemClickListener onItemClickListener;

    public EasyGridProvider(Context mContext) {
        this(mContext, null);
    }

    private int getDisplayWidthInPx(Context context) {
        Display display = DisplayManager.getInstance().getDefaultDisplay(context).get();
        Point point = new Point();
        display.getSize(point);
        return (int) point.getPointX();
    }

    /**
     * EasyGridProvider.
     *
     * @param context 上下文
     * @param data    数据源
     */
    public EasyGridProvider(Context context, List<T> data) {
        this.mContext = context;
        this.data = data;
        this.displayWidth = getDisplayWidthInPx(context);
    }

    /**
     * updateDisplayWidth.
     *
     * @param displayOrientation displayOrientation
     */
    public void updateDisplayWidth(AbilityInfo.DisplayOrientation displayOrientation) {
        this.displayWidth = getDisplayWidthInPx(mContext);
    }

    /**
     * setNumColumns.
     *
     * @param numColumns numColumns
     */
    public void setNumColumns(int numColumns) {
        this.numColumns = numColumns;
    }

    /**
     * setGridSpacing.
     *
     * @param gridSpacing gridSpacing
     */
    public void setGridSpacing(float gridSpacing) {
        this.gridSpacing = (int) (gridSpacing / 2);
    }

    /**
     * setData .
     *
     * @param data data
     */
    public void setData(List<T> data) {
        this.data = data;
        notifyDataChanged();
    }

    /**
     * getData .
     *
     * @return List
     */
    public List<T> getData() {
        return data;
    }

    /**
     * getDataCount.
     *
     * @return int
     */
    public int getDataCount() {
        return data != null ? data.size() : 0;
    }

    /**
     * getListPosition.
     *
     * @param position position
     * @return int
     */
    public int getListPosition(int position) {
        return position / numColumns;
    }

    @Override
    public int getCount() {
        if (data != null) {
            return data.size() % numColumns == 0 ? data.size() / numColumns : data.size() / numColumns + 1;
        } else {
            return 0;
        }
    }

    @Override
    public T getItem(int position) {
        if (data != null) {
            return data.get(position);
        } else {
            return null;
        }
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getItemComponentType(int position) {
        if (position >= getDataCount()) {
            return ITEM_EMPTY_TYPE;
        }
        return ITEM_DEFAULT_TYPE;
    }

    private void binView(ViewHolder viewHolder, int itemPosition) {
        int itemComponentType = getItemComponentType(itemPosition);
        Component itemView = viewHolder.getItemView();
        viewHolder.setAdapterPosition(itemPosition);
        if (itemComponentType == ITEM_DEFAULT_TYPE) {
            itemView.setClickedListener(component1 -> {
                if (onItemClickListener != null) {
                    onItemClickListener.onItemClick(component1, itemPosition);
                }
            });
            bind(viewHolder, getItem(itemPosition), itemPosition);
        } else {
            itemView.setClickedListener(null);
            bindPlaceholder(viewHolder, itemPosition);
        }
    }

    private void setMargins(Component childConvertComponent, int itemPosition) {
        if (gridSpacing != 0) {
            if ((itemPosition + 1) % numColumns == 0) {
                childConvertComponent.setMarginsLeftAndRight(gridSpacing, 0);
            } else if ((itemPosition) % numColumns == 0) {
                childConvertComponent.setMarginsLeftAndRight(0, gridSpacing);
            } else {
                childConvertComponent.setMarginsLeftAndRight(gridSpacing, gridSpacing);
            }
            childConvertComponent.setMarginsTopAndBottom(gridSpacing, gridSpacing);
        }
    }

    @Override
    public Component getComponent(int position, Component convertComponent, ComponentContainer parent) {
        if (convertComponent == null) {
            convertComponent = new DirectionalLayout(mContext);
            ((DirectionalLayout) convertComponent).setOrientation(Component.HORIZONTAL);
            ComponentContainer.LayoutConfig layoutConfig = convertComponent.getLayoutConfig();
            layoutConfig.width = DependentLayout.LayoutConfig.MATCH_PARENT;
            layoutConfig.height = DependentLayout.LayoutConfig.MATCH_CONTENT;
            convertComponent.setLayoutConfig(layoutConfig);
        }

        DirectionalLayout convertDirectionalLayout = (DirectionalLayout) convertComponent;

        for (int i = 0; i < numColumns; i++) {

            int itemPosition = position * numColumns + i;

            int itemComponentType = getItemComponentType(itemPosition);

            Component childConvertComponent = convertDirectionalLayout.getComponentAt(i);
            ViewHolder viewHolder = null;
            if (childConvertComponent == null) {
                childConvertComponent = LayoutScatter.getInstance(mContext).parse(getLayoutId(),
                        null, false);
                viewHolder = new ViewHolder(childConvertComponent, itemComponentType);
                childConvertComponent.setTag(viewHolder);
                convertDirectionalLayout.addComponent(childConvertComponent);
                int height = (displayWidth - numColumns * gridSpacing) / numColumns;
                ComponentContainer.LayoutConfig layoutConfig = new DirectionalLayout.LayoutConfig(0,
                        height, LayoutAlignment.TOP, 1);
                childConvertComponent.setLayoutConfig(layoutConfig);
                setMargins(childConvertComponent, itemPosition);
            } else {
                viewHolder = (ViewHolder) childConvertComponent.getTag();
            }
            binView(viewHolder, itemPosition);

        }
        return convertComponent;
    }

    /**
     * setOnItemClickListener.
     *
     * @param onItemClickListener onItemClickListener
     */
    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    /**
     * bind .
     *
     * @param holder   holder
     * @param item     item
     * @param position position
     */
    protected abstract void bind(ViewHolder holder, T item, int position);

    /**
     * bindPlaceholder.
     *
     * @param holder   holder
     * @param position position
     */
    protected abstract void bindPlaceholder(ViewHolder holder, int position);

    /**
     * getLayoutId .
     *
     * @return int
     */
    protected abstract int getLayoutId();

    /**
     * ViewHolder
     */
    public static class ViewHolder {
        private HashMap<Integer, Component> mViews = new HashMap<>();
        private Component itemView;
        private int adapterPosition;
        private int componentType;

        ViewHolder(Component component) {
            this(component, ITEM_DEFAULT_TYPE);
        }

        ViewHolder(Component component, int componentType) {
            this.itemView = component;
            this.componentType = componentType;
        }

        /**
         * setAdapterPosition.
         *
         * @param adapterPosition adapterPosition
         */
        public void setAdapterPosition(int adapterPosition) {
            this.adapterPosition = adapterPosition;
        }

        /**
         * getAdapterPosition.
         *
         * @return int
         */
        public int getAdapterPosition() {
            return adapterPosition;
        }

        /**
         * getComponentType.
         *
         * @return int
         */
        public int getComponentType() {
            return componentType;
        }

        /**
         * getItemView .
         *
         * @return Component
         */
        public Component getItemView() {
            return itemView;
        }

        /**
         * setText .
         *
         * @param viewId viewId
         * @param text   text
         * @return ViewHolder
         */
        public ViewHolder setText(int viewId, String text) {
            ((Text) getView(viewId)).setText(text);
            return this;
        }

        /**
         * getView
         *
         * @param viewId viewId
         * @param <E>    e
         * @return Component
         */
        public <E extends Component> E getView(int viewId) {
            return (E) mViews.computeIfAbsent(viewId, id -> itemView.findComponentById(id));
        }

    }

    /**
     * OnItemClickListener.
     */
    public static abstract class OnItemClickListener {
        /**
         * onItemClick
         *
         * @param component component
         * @param position  position
         */
        public abstract void onItemClick(Component component, int position);
    }
}
